package cn.tedu.mall.seckill.service.impl;

import cn.tedu.mall.common.exception.CoolSharkServiceException;
import cn.tedu.mall.common.restful.JsonPage;
import cn.tedu.mall.common.restful.ResponseCode;
import cn.tedu.mall.pojo.product.vo.SpuDetailStandardVO;
import cn.tedu.mall.pojo.product.vo.SpuStandardVO;
import cn.tedu.mall.pojo.seckill.model.SeckillSpu;
import cn.tedu.mall.pojo.seckill.vo.SeckillSpuDetailSimpleVO;
import cn.tedu.mall.pojo.seckill.vo.SeckillSpuVO;
import cn.tedu.mall.product.service.seckill.IForSeckillSpuService;
import cn.tedu.mall.seckill.mapper.SeckillSpuMapper;
import cn.tedu.mall.seckill.service.ISeckillSpuService;
import cn.tedu.mall.seckill.utils.RedisBloomUtils;
import cn.tedu.mall.seckill.utils.SeckillCacheUtils;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class SeckillSpuServiceImpl implements ISeckillSpuService {

    @Autowired
    private SeckillSpuMapper spuMapper;
    @Autowired
    private RedisTemplate redisTemplate;
    @DubboReference
    private IForSeckillSpuService dubboSeckillSpuService;
    @Autowired
    private RedisBloomUtils redisBloomUtils;

    @Override
    public JsonPage<SeckillSpuVO> listSeckillSpus(Integer page, Integer pageSize) {

        PageHelper.startPage(page, pageSize);
        List<SeckillSpu> seckillSpus = spuMapper.findSeckillSpus();

        //商品信息整合
        List<SeckillSpuVO> seckillSpuVOS = new ArrayList<>();
        for(SeckillSpu seckillSpu : seckillSpus){
            SeckillSpuVO seckillSpuVO = new SeckillSpuVO();
            //商品标准信息
            SpuStandardVO spuStandardVO = dubboSeckillSpuService.getSpuById(seckillSpu.getSpuId());
            BeanUtils.copyProperties(spuStandardVO, seckillSpuVO);
            seckillSpuVO.setSeckillListPrice(seckillSpu.getListPrice());
            seckillSpuVO.setStartTime(seckillSpu.getStartTime());
            seckillSpuVO.setEndTime(seckillSpu.getEndTime());

            seckillSpuVOS.add(seckillSpuVO);
        }
        return JsonPage.restPage(PageInfo.of(seckillSpuVOS));
    }

    @Override
    public SeckillSpuVO getSeckillSpu(Long spuId) {
        //布隆过滤器
        String bloomFilterKey = SeckillCacheUtils.getBloomFilterKey(LocalDate.now());
        log.info("{}", bloomFilterKey);
        if(!redisBloomUtils.bfexists(bloomFilterKey,spuId+"")){
            // 进入这个if表示当前商品id不在布隆过滤器中
            // 防止缓存穿透,抛出异常
            throw new CoolSharkServiceException(
                    ResponseCode.NOT_FOUND,"您访问的商品不存在(布隆过滤器生效)");
        }


        String seckillSpuVOKey = SeckillCacheUtils.getSeckillSpuVOKey(spuId);
        SeckillSpuVO seckillSpuVO;
        if(redisTemplate.hasKey(seckillSpuVOKey)){
            log.info("key为{}的数据存在于Redis中，查询中...",seckillSpuVOKey);
            seckillSpuVO = (SeckillSpuVO) redisTemplate.boundValueOps(seckillSpuVOKey)
                    .get();
        }else{
            SeckillSpu seckillSpu = spuMapper.findSeckillSpuBySpuId(spuId);
            if(seckillSpu==null){
                throw new CoolSharkServiceException(ResponseCode.NOT_FOUND,
                        "你访问的商品信息不存在！");
            }
            //seckillSpuVO赋值
            SpuStandardVO spuStandardVO = dubboSeckillSpuService.getSpuById(spuId);
            seckillSpuVO = new SeckillSpuVO();
            BeanUtils.copyProperties(spuStandardVO, seckillSpuVO);
            seckillSpuVO.setSeckillListPrice(seckillSpu.getListPrice());
            seckillSpuVO.setStartTime(seckillSpu.getStartTime());
            seckillSpuVO.setEndTime(seckillSpu.getEndTime());
            //保存到Redis。
            redisTemplate.boundValueOps(seckillSpuVOKey)
                    .set(seckillSpuVO,
                            1000*60*5, TimeUnit.MILLISECONDS);
        }
        //url发送到前端后，让前端使用它向后端发起秒杀订单请求。
        LocalDateTime now = LocalDateTime.now();
        if(seckillSpuVO.getStartTime().isAfter(now)
                || seckillSpuVO.getEndTime().isBefore(now)){
            throw new CoolSharkServiceException(ResponseCode.NOT_FOUND,
                    "商品未在秒杀时间内");
        }
        String randCodeKey = SeckillCacheUtils.getRandCodeKey(spuId);
        if(!redisTemplate.hasKey(randCodeKey)){
            throw new CoolSharkServiceException(ResponseCode.NOT_FOUND,
                    "随机码不存在！");
        }
        String randCode = redisTemplate.boundValueOps(randCodeKey)
                .get().toString();
        seckillSpuVO.setUrl("/seckill/"+randCode);
        return seckillSpuVO;
    }

    public static final String SECKLILL_SPU_DETAIL_PREFIX = "seckill:spu:detail:";
    @Override
    public SeckillSpuDetailSimpleVO getSeckillSpuDetail(Long spuId) {

        String spuDetailKey = SECKLILL_SPU_DETAIL_PREFIX+spuId;
        SeckillSpuDetailSimpleVO simpleVO;
        if(redisTemplate.hasKey(spuDetailKey)){
            simpleVO = (SeckillSpuDetailSimpleVO) redisTemplate.boundValueOps(spuDetailKey)
                    .get();
        }else{
            //访问数据库
            SpuDetailStandardVO spuDetailStandardVO = dubboSeckillSpuService.getSpuDetailById(spuId);
            simpleVO = new SeckillSpuDetailSimpleVO();
            BeanUtils.copyProperties(spuDetailStandardVO, simpleVO);
            redisTemplate.boundValueOps(spuDetailKey).set(simpleVO,
                    1000*60*5, TimeUnit.MILLISECONDS);
        }
        return simpleVO;
    }
}
